﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;

namespace Mesuro.Common {
	public class SeriesTransformCollection : ISeriesTransform, IEnumerable<ISeriesTransform> {

		public class TransformCollectionMarker {
			internal WeakReference Node { get; set; }
			internal TransformCollectionMarker( LinkedListNode<ISeriesTransform> node ) {
				this.Node = new WeakReference( node );
			}
		}

		private LinkedList<ISeriesTransform> list = new LinkedList<ISeriesTransform>();

		private ISeries _source;
		public ISeries Source {
			get { return _source; }
			set {
				_source = value;
				if (list.Count > 0)
					list.Last.Value.Source = value;
				else if (_source != null)
					_source.DataChanged += HeadDataChanged;
			}
		}

		public ISeries Head {
			get { return (list.First == null) ? (_source ?? DefaultImmutableSeries.EmptySeries) : list.First.Value; }
		}

		public IChannelCollection Channels {
			get { return Head.Channels; }
		}

		public string Name { get; set; }

		public int Dimensions {
			get { return Head.Dimensions; }
		}

		public event NotifySeriesChangedEventHandler DataChanged;

		public TransformCollectionMarker Push( ISeriesTransform transform ) {
			if (transform == null) throw new ArgumentNullException();

			if (Head != null)
				Head.DataChanged -= HeadDataChanged;

			transform.Source = Head;

			transform.DataChanged += HeadDataChanged;

			TransformCollectionMarker ret = new TransformCollectionMarker( list.AddFirst( transform ) );

			HeadDataChanged( this, new NotifySeriesChangedEventArgs( this, NotifySeriesChangedAction.DataReplaced ) );

			return ret;
		}
		public TransformCollectionMarker InsertBefore( TransformCollectionMarker marker, ISeriesTransform transform ) {
			if (marker == null) return Push( transform );

			LinkedListNode<ISeriesTransform> reference = (LinkedListNode<ISeriesTransform>)marker.Node.Target;
			if (reference == null) return Push( transform );

			reference.Value.Source = transform;

			if (reference.Next != null)
				transform.Source = reference.Next.Value;

			LinkedListNode<ISeriesTransform> ret = new LinkedListNode<ISeriesTransform>( transform );
			list.AddAfter( reference, new LinkedListNode<ISeriesTransform>( transform ) );

			if (list.Last.Value == transform)
				list.Last.Value.Source = _source;

			if (Head == transform) {
				reference.Value.DataChanged -= HeadDataChanged;
				Head.DataChanged += HeadDataChanged;
			}

			HeadDataChanged( this, new NotifySeriesChangedEventArgs( this, NotifySeriesChangedAction.DataReplaced ) );

			return new TransformCollectionMarker( ret );
		}
		public TransformCollectionMarker Replace( TransformCollectionMarker marker, ISeriesTransform transform ) {
			if (marker == null || !marker.Node.IsAlive) return Push( transform );

			LinkedListNode<ISeriesTransform> reference = (LinkedListNode<ISeriesTransform>)marker.Node.Target;
			if (reference == null) return Push( transform );

			if (reference.Previous != null)
				reference.Previous.Value.Source = transform;

			if (reference.Next != null)
				transform.Source = reference.Next.Value;

			LinkedListNode<ISeriesTransform> ret = new LinkedListNode<ISeriesTransform>( transform );
			list.AddAfter( reference, ret );
			list.Remove( reference );

			//if (list.Last.Value == transform)
			//    list.Last.Value.Source = _source;

			if (Head == transform) {
				reference.Value.DataChanged -= HeadDataChanged;
				Head.DataChanged += HeadDataChanged;
			}

			HeadDataChanged( this, new NotifySeriesChangedEventArgs( this, NotifySeriesChangedAction.DataReplaced ) );

			return new TransformCollectionMarker( ret );
		}

		public void Remove( TransformCollectionMarker transformMarker ) {
			LinkedListNode<ISeriesTransform> node = (LinkedListNode<ISeriesTransform>)transformMarker.Node.Target;
			node.Previous.Value.Source = node.Next.Value;
			if (Head == node.Value) {
				node.Value.DataChanged -= HeadDataChanged;
				list.Remove( node );
				if (Head != null)
					Head.DataChanged += HeadDataChanged;
			} else
				list.Remove( node );

		}

		private void HeadDataChanged( object sender, NotifySeriesChangedEventArgs e ) {
			if (DataChanged != null)
				DataChanged( this, e );
		}

		public IEnumerator<Vector> GetDataEnumerator() {
			return Head.GetEnumerator();
		}
		public IEnumerator<ISeriesTransform> GetTransformEnumerator() {
			return list.Reverse().GetEnumerator();
		}

		IEnumerator<Vector> IEnumerable<Vector>.GetEnumerator() {
			return GetDataEnumerator();
		}
		IEnumerator<ISeriesTransform> IEnumerable<ISeriesTransform>.GetEnumerator() {
			return GetTransformEnumerator();
		}
		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
			throw new NotImplementedException( "This function call is ambiguous, please use one of the two generic implementationg of GetEnumerator" );
		}


		public object Clone() {
			return MemberwiseClone();
		}
	}
}
